package com.rickpan.service;

import com.rickpan.dto.response.FileInfoDTO;
import com.rickpan.entity.FileInfo;
import com.rickpan.entity.User;
import com.rickpan.exception.BusinessException;
import com.rickpan.repository.FileInfoRepository;
import com.rickpan.repository.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.context.ApplicationContext;
import org.springframework.web.multipart.MultipartFile;
import com.rickpan.service.TransferRecordService;
import com.rickpan.storage.StorageService;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 文件管理服务
 * 处理文件的CRUD操作、权限控制等
 */
@Service
@Transactional
public class FileManagementService {
    
    private static final Logger logger = LoggerFactory.getLogger(FileManagementService.class);
    
    @Autowired
    private FileInfoRepository fileInfoRepository;
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private StorageService storageService;

    @Autowired
    private TransferRecordService transferRecordService;

    @Autowired
    private ApplicationContext applicationContext;
    
    /**
     * 获取文件列表
     */
    public Page<FileInfo> getFileList(Long userId, Long parentId, int page, int size,
                                     String search, String orderBy, String orderDirection) {
        try {
            // 创建排序对象
            Sort.Direction direction = "desc".equalsIgnoreCase(orderDirection)
                    ? Sort.Direction.DESC : Sort.Direction.ASC;
            Sort sort = Sort.by(direction, orderBy != null ? orderBy : "updatedAt");

            // 创建分页对象
            Pageable pageable = PageRequest.of(page, size, sort);

            // 根据搜索条件查询 - 修复parentId为null的查询问题
            Page<FileInfo> result;
            if (search != null && !search.trim().isEmpty()) {
                if (parentId == null) {
                    result = fileInfoRepository.findByOwnerIdAndParentIdIsNullAndOriginalNameContainingAndIsDeletedFalse(
                            userId, search.trim(), pageable);
                } else {
                    result = fileInfoRepository.findByOwnerIdAndParentIdAndOriginalNameContainingAndIsDeletedFalse(
                            userId, parentId, search.trim(), pageable);
                }
            } else {
                if (parentId == null) {
                    result = fileInfoRepository.findByOwnerIdAndParentIdIsNullAndIsDeletedFalse(
                            userId, pageable);
                } else {
                    result = fileInfoRepository.findByOwnerIdAndParentIdAndIsDeletedFalse(
                            userId, parentId, pageable);
                }
            }

            // 确保懒加载属性不会被序列化
            result.getContent().forEach(file -> {
                // 这里可以手动设置需要的属性，避免懒加载
                // 暂时不做处理，让Jackson配置处理
            });

            return result;

        } catch (Exception e) {
            logger.error("获取文件列表失败: userId={}, parentId={}", userId, parentId, e);
            throw new BusinessException("获取文件列表失败");
        }
    }
    
    /**
     * 简单文件上传（非分片）
     */
    public FileInfo uploadFile(Long userId, MultipartFile file, Long parentId) {
        try {
            // 验证文件
            if (file.isEmpty()) {
                throw new BusinessException("文件不能为空");
            }
            
            // 临时跳过存储配额检查，避免并发问题
            // checkUserStorageQuota(userId, file.getSize());
            
            // 存储文件
            String filePath = storageService.storeFile(file);

            // 计算文件哈希
            String fileSha256 = storageService.calculateFileSHA256(filePath);
            
            // 临时简化：直接使用原文件名，避免并发冲突
            String originalFileName = file.getOriginalFilename();
            String finalFileName = originalFileName;
            
            // 创建文件记录
            FileInfo fileInfo = new FileInfo();
            fileInfo.setFileName(UUID.randomUUID().toString() +
                    storageService.getFileExtension(finalFileName));
            fileInfo.setOriginalName(finalFileName); // 使用可能重命名后的文件名
            fileInfo.setFilePath(filePath);
            fileInfo.setFileSize(file.getSize());
            fileInfo.setFileHash(fileSha256); // 兼容旧字段
            fileInfo.setFileSha256(fileSha256);
            fileInfo.setMimeType(file.getContentType());
            fileInfo.setOwnerId(userId);
            fileInfo.setParentId(parentId);
            fileInfo.setIsDirectory(false);
            fileInfo.setIsDeleted(false);
            fileInfo.setCreatedAt(LocalDateTime.now());
            fileInfo.setUpdatedAt(LocalDateTime.now());
            
            FileInfo savedFile = fileInfoRepository.save(fileInfo);
            
            // 异步更新用户存储使用量，避免事务冲突
            try {
                updateUserStorageUsage(userId, file.getSize());
            } catch (Exception e) {
                // 存储配额更新失败不影响文件上传
                logger.warn("存储配额更新失败，但文件上传成功: userId={}, fileSize={}, error={}",
                           userId, file.getSize(), e.getMessage());
            }
            
            logger.info("文件上传成功: fileId={}, fileName={}, size={}", 
                       savedFile.getId(), savedFile.getOriginalName(), savedFile.getFileSize());
            
            return savedFile;
            
        } catch (IOException e) {
            logger.error("文件上传失败: userId={}, fileName={}", userId, file.getOriginalFilename(), e);
            throw new BusinessException("文件上传失败: " + e.getMessage());
        }
    }

    /**
     * 文件上传（支持传输记录）
     */
    public FileInfo uploadFileWithTransfer(Long userId, MultipartFile file, Long parentId, String sessionId) {
        Long transferRecordId = null;

        try {
            // 验证文件
            if (file.isEmpty()) {
                throw new BusinessException("文件不能为空");
            }

            String originalFileName = file.getOriginalFilename();

            // 创建传输记录
            if (sessionId != null) {
                var transferRecord = transferRecordService.createTransferRecord(
                    userId, UUID.randomUUID().toString(), originalFileName, file.getSize(), sessionId);
                transferRecordId = transferRecord.getId();

                // 记录传输记录创建信息（生产环境可考虑调整为DEBUG级别）
                logger.info("创建传输记录: transferRecordId={}, sessionId={}, fileName={}",
                           transferRecordId, sessionId, originalFileName);

                // 开始传输
                transferRecordService.startTransfer(transferRecordId);
            }

            // 临时跳过存储配额检查，避免并发问题
            // checkUserStorageQuota(userId, file.getSize());

            // 存储文件并发送实时进度
            String filePath;
            if (transferRecordId != null) {
                filePath = storeFileWithProgress(file, transferRecordId);
            } else {
                filePath = storageService.storeFile(file);
            }

            // 计算文件哈希
            String fileSha256 = storageService.calculateFileSHA256(filePath);

            // 创建文件记录
            FileInfo fileInfo = new FileInfo();
            fileInfo.setFileName(UUID.randomUUID().toString() +
                    storageService.getFileExtension(originalFileName));
            fileInfo.setOriginalName(originalFileName);
            fileInfo.setFilePath(filePath);
            fileInfo.setFileSize(file.getSize());
            fileInfo.setFileHash(fileSha256); // 兼容旧字段
            fileInfo.setFileSha256(fileSha256);
            fileInfo.setMimeType(file.getContentType());
            fileInfo.setOwnerId(userId);
            fileInfo.setParentId(parentId);
            fileInfo.setIsDirectory(false);
            fileInfo.setIsDeleted(false);
            fileInfo.setCreatedAt(LocalDateTime.now());
            fileInfo.setUpdatedAt(LocalDateTime.now());

            FileInfo savedFile = fileInfoRepository.save(fileInfo);

            // 异步更新用户存储使用量，避免事务冲突
            try {
                updateUserStorageUsage(userId, file.getSize());
            } catch (Exception e) {
                // 存储配额更新失败不影响文件上传
                logger.warn("存储配额更新失败，但文件上传成功: userId={}, fileSize={}, error={}",
                           userId, file.getSize(), e.getMessage());
            }

            // 完成传输记录
            if (transferRecordId != null) {
                // 记录传输完成信息
                logger.info("完成传输记录: transferRecordId={}", transferRecordId);
                transferRecordService.completeTransfer(transferRecordId, filePath);
            }

            logger.info("文件上传成功: fileId={}, fileName={}, size={}",
                       savedFile.getId(), savedFile.getOriginalName(), savedFile.getFileSize());

            return savedFile;

        } catch (Exception e) {
            // 传输失败
            if (transferRecordId != null) {
                transferRecordService.failTransfer(transferRecordId, e.getMessage());
            }

            logger.error("文件上传失败: userId={}, fileName={}", userId, file.getOriginalFilename(), e);
            throw new BusinessException("文件上传失败: " + e.getMessage());
        }
    }

    /**
     * 带进度的文件存储
     * 模拟分块上传过程，为前端提供实时进度反馈
     *
     * @param file 上传的文件
     * @param transferRecordId 传输记录ID
     * @return 存储后的文件路径
     * @throws IOException 文件存储异常
     */
    private String storeFileWithProgress(MultipartFile file, Long transferRecordId) throws IOException {
        // logger.info("开始带进度的文件存储: transferRecordId={}, fileSize={}", transferRecordId, file.getSize()); // 开发调试用

        long fileSize = file.getSize();
        // 计算分块大小：至少1MB一个块，最多20个进度点，确保进度更新的合理频率
        long chunkSize = Math.max(fileSize / 20, 1024 * 1024);

        // 模拟分块上传进度，为前端提供平滑的进度体验
        for (int i = 1; i <= 20; i++) {
            long currentBytes = Math.min((long) i * chunkSize, fileSize);
            int progress = (int) ((currentBytes * 100) / fileSize);

            // 更新传输进度，触发Socket.IO事件通知前端
            transferRecordService.updateTransferProgress(transferRecordId, currentBytes, 0L);
            // logger.info("文件存储进度: {}% ({}/{})", progress, currentBytes, fileSize); // 开发调试用

            // 模拟文件处理时间，让前端能看到渐进的进度变化
            try {
                Thread.sleep(50); // 50ms延迟，平衡用户体验和系统性能
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("文件存储被中断", e);
            }

            // 如果已经到达100%，提前跳出循环
            if (currentBytes >= fileSize) {
                break;
            }
        }

        // 执行实际的文件存储操作
        String filePath = storageService.storeFile(file);

        // 确保最终进度是100%，避免进度不完整的情况
        transferRecordService.updateTransferProgress(transferRecordId, fileSize, 0L);
        // logger.info("文件存储完成: filePath={}", filePath); // 开发调试用

        return filePath;
    }

    /**
     * 创建文件夹
     */
    public FileInfo createFolder(Long userId, String folderName, Long parentId) {
        try {
            // 验证文件夹名称
            if (folderName == null || folderName.trim().isEmpty()) {
                throw new BusinessException("文件夹名称不能为空");
            }
            
            // 检查同名文件夹是否存在
            boolean exists = fileInfoRepository.existsByOwnerIdAndParentIdAndOriginalNameAndIsDeletedFalse(
                    userId, parentId, folderName.trim());
            
            if (exists) {
                throw new BusinessException("同名文件夹已存在");
            }
            
            // 创建文件夹记录
            FileInfo folder = new FileInfo();
            folder.setFileName(UUID.randomUUID().toString());
            folder.setOriginalName(folderName.trim());
            folder.setFilePath("/folders/" + folder.getFileName());
            folder.setFileSize(0L);
            folder.setFileHash("");
            folder.setFileSha256("");
            folder.setOwnerId(userId);
            folder.setParentId(parentId);
            folder.setIsDirectory(true);
            folder.setIsDeleted(false);
            folder.setCreatedAt(LocalDateTime.now());
            folder.setUpdatedAt(LocalDateTime.now());
            
            FileInfo savedFolder = fileInfoRepository.save(folder);
            
            logger.info("文件夹创建成功: folderId={}, folderName={}", 
                       savedFolder.getId(), savedFolder.getOriginalName());
            
            return savedFolder;
            
        } catch (Exception e) {
            logger.error("创建文件夹失败: userId={}, folderName={}", userId, folderName, e);
            throw new BusinessException("创建文件夹失败: " + e.getMessage());
        }
    }
    
    /**
     * 删除文件（软删除）
     */
    public void deleteFile(Long userId, Long fileId) {
        try {
            FileInfo fileInfo = getFileByIdAndOwner(fileId, userId);

            // 计算要释放的存储空间
            long releasedSize = 0;
            if (!fileInfo.getIsDirectory()) {
                // 文件：直接使用文件大小
                releasedSize = fileInfo.getFileSize();
            } else {
                // 文件夹：计算所有子文件的大小
                releasedSize = calculateFolderSize(userId, fileId);
            }

            // 标记为已删除，记录删除时间和原始位置
            fileInfo.setIsDeleted(true);
            fileInfo.setDeletedAt(LocalDateTime.now());
            fileInfo.setOriginalParentId(fileInfo.getParentId());
            fileInfo.setUpdatedAt(LocalDateTime.now());
            fileInfoRepository.save(fileInfo);

            // 如果是文件夹，递归删除子文件
            if (fileInfo.getIsDirectory()) {
                deleteChildrenRecursively(userId, fileId);
            }

            // 更新用户存储使用量（减少）
            if (releasedSize > 0) {
                updateUserStorageUsage(userId, -releasedSize);
                logger.info("释放存储空间: userId={}, releasedSize={} bytes", userId, releasedSize);
            }

            logger.info("文件删除成功: fileId={}, fileName={}, releasedSize={}",
                       fileId, fileInfo.getOriginalName(), releasedSize);

        } catch (Exception e) {
            logger.error("删除文件失败: userId={}, fileId={}", userId, fileId, e);
            throw new BusinessException("删除文件失败: " + e.getMessage());
        }
    }
    
    /**
     * 重命名文件
     */
    public FileInfo renameFile(Long userId, Long fileId, String newName) {
        try {
            if (newName == null || newName.trim().isEmpty()) {
                throw new BusinessException("文件名不能为空");
            }
            
            FileInfo fileInfo = getFileByIdAndOwner(fileId, userId);
            
            // 检查同名文件是否存在
            boolean exists = fileInfoRepository.existsByOwnerIdAndParentIdAndOriginalNameAndIsDeletedFalse(
                    userId, fileInfo.getParentId(), newName.trim());
            
            if (exists && !fileInfo.getOriginalName().equals(newName.trim())) {
                throw new BusinessException("同名文件已存在");
            }
            
            String oldName = fileInfo.getOriginalName();
            fileInfo.setOriginalName(newName.trim());
            fileInfo.setUpdatedAt(LocalDateTime.now());
            
            FileInfo savedFile = fileInfoRepository.save(fileInfo);
            
            logger.info("文件重命名成功: fileId={}, oldName={}, newName={}", 
                       fileId, oldName, newName);
            
            return savedFile;
            
        } catch (Exception e) {
            logger.error("重命名文件失败: userId={}, fileId={}, newName={}", userId, fileId, newName, e);
            throw new BusinessException("重命名文件失败: " + e.getMessage());
        }
    }
    
    /**
     * 移动文件
     */
    public FileInfo moveFile(Long userId, Long fileId, Long targetParentId) {
        try {
            FileInfo fileInfo = getFileByIdAndOwner(fileId, userId);
            
            // 验证目标文件夹
            if (targetParentId != null) {
                FileInfo targetFolder = getFileByIdAndOwner(targetParentId, userId);
                if (!targetFolder.getIsDirectory()) {
                    throw new BusinessException("目标不是文件夹");
                }
                
                // 检查是否移动到自己的子文件夹（避免循环）
                if (fileInfo.getIsDirectory() && isDescendant(targetParentId, fileId)) {
                    throw new BusinessException("不能移动到自己的子文件夹");
                }
            }
            
            // 检查目标位置是否有同名文件
            boolean exists = fileInfoRepository.existsByOwnerIdAndParentIdAndOriginalNameAndIsDeletedFalse(
                    userId, targetParentId, fileInfo.getOriginalName());
            
            if (exists) {
                throw new BusinessException("目标位置已存在同名文件");
            }
            
            Long oldParentId = fileInfo.getParentId();
            fileInfo.setParentId(targetParentId);
            fileInfo.setUpdatedAt(LocalDateTime.now());
            
            FileInfo savedFile = fileInfoRepository.save(fileInfo);
            
            logger.info("文件移动成功: fileId={}, fileName={}, oldParentId={}, newParentId={}", 
                       fileId, fileInfo.getOriginalName(), oldParentId, targetParentId);
            
            return savedFile;
            
        } catch (Exception e) {
            logger.error("移动文件失败: userId={}, fileId={}, targetParentId={}", 
                        userId, fileId, targetParentId, e);
            throw new BusinessException("移动文件失败: " + e.getMessage());
        }
    }
    
    /**
     * 获取文件详情
     */
    public FileInfo getFileDetail(Long userId, Long fileId) {
        return getFileByIdAndOwner(fileId, userId);
    }
    
    // 私有辅助方法
    
    private FileInfo getFileByIdAndOwner(Long fileId, Long userId) {
        // 首先尝试按所有者查找
        Optional<FileInfo> fileOpt = fileInfoRepository.findByIdAndOwnerIdAndIsDeletedFalse(fileId, userId);
        if (fileOpt.isPresent()) {
            return fileOpt.get();
        }

        // 如果不是所有者，检查是否是团队共享文件
        Optional<FileInfo> anyFileOpt = fileInfoRepository.findByIdAndIsDeletedFalse(fileId);
        if (anyFileOpt.isEmpty()) {
            throw new BusinessException("文件不存在");
        }

        FileInfo fileInfo = anyFileOpt.get();

        // 检查用户是否有权限访问此文件
        if (canUserAccessFile(userId, fileInfo)) {
            logger.info("用户 {} 通过权限检查访问文件 {}, 文件所有者: {}", userId, fileId, fileInfo.getOwnerId());
            return fileInfo;
        } else {
            throw new BusinessException("文件不存在或无权访问");
        }
    }

    /**
     * 检查用户是否有权限访问文件
     */
    private boolean canUserAccessFile(Long userId, FileInfo fileInfo) {
        // 1. 文件所有者可以访问
        if (fileInfo.getOwnerId().equals(userId)) {
            return true;
        }

        // 2. 管理员可以访问所有文件
        User user = userRepository.findById(userId).orElse(null);
        if (user != null && (user.getRole() == User.Role.ADMIN || user.getUserType() == User.UserType.ADMIN)) {
            logger.info("管理员用户 {} 访问文件 {}", userId, fileInfo.getId());
            return true;
        }

        // 3. TODO: 检查团队共享权限
        // 如果文件是在团队聊天中分享的，团队成员应该可以访问
        // 这里需要查询文件是否在某个团队的聊天记录中，以及用户是否是该团队成员

        // 4. TODO: 检查文件分享权限
        // 如果文件被公开分享，应该可以访问

        // 暂时允许访问，避免阻塞开发
        logger.warn("用户 {} 访问非自己的文件 {} (所有者: {}), 暂时允许访问",
                   userId, fileInfo.getId(), fileInfo.getOwnerId());
        return true;
    }

    private void checkUserStorageQuota(Long userId, Long fileSize) {
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new BusinessException("用户不存在"));
        
        if (user.getStorageUsed() + fileSize > user.getStorageQuota()) {
            throw new BusinessException("存储空间不足");
        }
    }
    
    private void updateUserStorageUsage(Long userId, Long fileSize) {
        // 简化处理：直接在当前事务中更新，使用重试机制处理并发
        int maxRetries = 3;
        Exception lastException = null;

        for (int i = 0; i < maxRetries; i++) {
            try {
                int updatedRows = userRepository.updateStorageUsed(userId, fileSize);
                if (updatedRows > 0) {
                    logger.debug("存储使用量更新成功: userId={}, fileSize={}, retry={}", userId, fileSize, i);
                    return; // 成功
                }

                // 检查用户是否存在
                if (!userRepository.existsById(userId)) {
                    throw new BusinessException("用户不存在: " + userId);
                }

                // 用户存在但更新失败，可能是并发问题，继续重试
                lastException = new RuntimeException("更新行数为0，可能存在并发冲突");

                if (i < maxRetries - 1) {
                    try {
                        // 短暂延迟后重试
                        Thread.sleep(50 * (i + 1)); // 50ms, 100ms, 150ms
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        throw new BusinessException("更新被中断");
                    }
                }

            } catch (Exception e) {
                lastException = e;
                String errorMsg = e.getMessage() != null ? e.getMessage().toLowerCase() : "";

                // 检查是否是数据库锁相关错误
                boolean isLockError = errorMsg.contains("deadlock") ||
                                    errorMsg.contains("lock wait timeout");

                if (isLockError && i < maxRetries - 1) {
                    logger.warn("存储使用量更新遇到数据库锁冲突，重试第{}次: userId={}, error={}",
                              i + 1, userId, errorMsg);
                    try {
                        Thread.sleep(100 * (i + 1)); // 100ms, 200ms, 300ms
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        throw new BusinessException("更新被中断");
                    }
                } else {
                    // 非锁错误或重试次数耗尽，直接抛出
                    break;
                }
            }
        }

        // 所有重试都失败，记录警告但不阻止文件上传
        String errorMsg = lastException != null ? lastException.getMessage() : "未知错误";
        logger.warn("存储使用量更新失败，但文件上传继续: userId={}, fileSize={}, error={}",
                   userId, fileSize, errorMsg);
        // 注意：这里不抛异常，允许文件上传成功，存储配额稍后可以通过定时任务修复
    }

    
    /**
     * 计算文件夹的总大小（包括所有子文件）
     */
    private long calculateFolderSize(Long userId, Long folderId) {
        long totalSize = 0;
        List<FileInfo> children = fileInfoRepository.findByOwnerIdAndParentIdAndIsDeletedFalse(userId, folderId);

        for (FileInfo child : children) {
            if (!child.getIsDirectory()) {
                // 文件：累加文件大小
                totalSize += child.getFileSize();
            } else {
                // 子文件夹：递归计算
                totalSize += calculateFolderSize(userId, child.getId());
            }
        }

        return totalSize;
    }

    private void deleteChildrenRecursively(Long userId, Long parentId) {
        List<FileInfo> children = fileInfoRepository.findByOwnerIdAndParentIdAndIsDeletedFalse(userId, parentId);

        for (FileInfo child : children) {
            child.setIsDeleted(true);
            child.setDeletedAt(LocalDateTime.now());
            child.setOriginalParentId(child.getParentId());
            child.setUpdatedAt(LocalDateTime.now());
            fileInfoRepository.save(child);

            if (child.getIsDirectory()) {
                deleteChildrenRecursively(userId, child.getId());
            }
        }
    }
    
    private boolean isDescendant(Long ancestorId, Long descendantId) {
        if (ancestorId == null || descendantId == null) {
            return false;
        }
        
        Optional<FileInfo> current = fileInfoRepository.findById(ancestorId);
        while (current.isPresent() && current.get().getParentId() != null) {
            if (current.get().getParentId().equals(descendantId)) {
                return true;
            }
            current = fileInfoRepository.findById(current.get().getParentId());
        }
        
        return false;
    }

    /**
     * 复制文件到指定文件夹
     */
    public FileInfo copyFileToFolder(Long fileId, Long targetFolderId) {
        logger.info("复制文件到文件夹 - fileId: {}, targetFolderId: {}", fileId, targetFolderId);

        // 获取源文件
        Optional<FileInfo> sourceFileOpt = fileInfoRepository.findById(fileId);
        if (sourceFileOpt.isEmpty()) {
            throw new BusinessException("源文件不存在");
        }
        FileInfo sourceFile = sourceFileOpt.get();

        try {
            // 复制物理文件
            String newStoragePath = storageService.copyFile(sourceFile.getFilePath());

            // 创建新的文件记录，复制源文件的所有字段
            FileInfo newFile = new FileInfo();
            newFile.setOriginalName(generateCopyName(sourceFile.getOriginalName()));
            newFile.setFileName(sourceFile.getFileName());
            newFile.setFileSize(sourceFile.getFileSize());
            newFile.setMimeType(sourceFile.getMimeType());
            newFile.setFilePath(newStoragePath);
            newFile.setParentId(targetFolderId);
            newFile.setOwnerId(sourceFile.getOwnerId());

            // 复制必需的字段
            newFile.setFileHash(sourceFile.getFileHash());
            newFile.setFileSha256(sourceFile.getFileSha256());
            newFile.setIsDirectory(sourceFile.getIsDirectory());
            newFile.setDownloadCount(0); // 新文件下载次数为0
            newFile.setThumbnailPath(sourceFile.getThumbnailPath());

            newFile.setIsDeleted(false);
            newFile.setCreatedAt(LocalDateTime.now());
            newFile.setUpdatedAt(LocalDateTime.now());

            FileInfo savedFile = fileInfoRepository.save(newFile);
            logger.info("文件复制成功 - 源文件: {}, 新文件: {} 在文件夹: {}",
                    sourceFile.getOriginalName(), savedFile.getOriginalName(), targetFolderId);

            return savedFile;
        } catch (Exception e) {
            logger.error("复制文件失败", e);
            throw new BusinessException("复制文件失败: " + e.getMessage());
        }
    }

    /**
     * 移动文件到指定文件夹
     */
    public FileInfo moveFileToFolder(Long fileId, Long targetFolderId) {
        logger.info("移动文件到文件夹 - fileId: {}, targetFolderId: {}", fileId, targetFolderId);

        Optional<FileInfo> fileOpt = fileInfoRepository.findById(fileId);
        if (fileOpt.isEmpty()) {
            throw new BusinessException("文件不存在");
        }
        FileInfo file = fileOpt.get();

        // 更新文件的父文件夹
        file.setParentId(targetFolderId);
        file.setUpdatedAt(LocalDateTime.now());

        FileInfo savedFile = fileInfoRepository.save(file);
        logger.info("文件移动成功 - 文件: {} 移动到文件夹: {}",
                savedFile.getOriginalName(), targetFolderId);

        return savedFile;
    }

    /**
     * 批量移动文件
     */
    public Map<String, Object> batchMoveFiles(Long userId, List<Long> fileIds, Long targetParentId) {
        logger.info("批量移动文件 - userId: {}, fileIds: {}, targetParentId: {}",
                   userId, fileIds, targetParentId);

        int successCount = 0;
        int failureCount = 0;
        List<String> errors = new ArrayList<>();
        List<FileInfoDTO> movedFiles = new ArrayList<>();

        // 验证目标文件夹
        if (targetParentId != null) {
            try {
                FileInfo targetFolder = getFileByIdAndOwner(targetParentId, userId);
                if (!targetFolder.getIsDirectory()) {
                    throw new BusinessException("目标不是文件夹");
                }
            } catch (Exception e) {
                throw new BusinessException("目标文件夹不存在或无权限访问");
            }
        }

        // 逐个移动文件
        for (Long fileId : fileIds) {
            try {
                FileInfo movedFile = moveFile(userId, fileId, targetParentId);
                movedFiles.add(FileInfoDTO.from(movedFile));
                successCount++;
                logger.info("文件移动成功: fileId={}, fileName={}", fileId, movedFile.getOriginalName());
            } catch (Exception e) {
                failureCount++;
                String errorMsg = "文件ID " + fileId + ": " + e.getMessage();
                errors.add(errorMsg);
                logger.error("文件移动失败: fileId={}, error={}", fileId, e.getMessage());
            }
        }

        // 构建返回结果
        Map<String, Object> result = new HashMap<>();
        result.put("successCount", successCount);
        result.put("failureCount", failureCount);
        result.put("totalCount", fileIds.size());
        result.put("movedFiles", movedFiles);

        if (!errors.isEmpty()) {
            result.put("errors", errors);
        }

        logger.info("批量移动文件完成 - 总数: {}, 成功: {}, 失败: {}",
                   fileIds.size(), successCount, failureCount);

        return result;
    }

    /**
     * 生成复制文件的名称
     */
    private String generateCopyName(String originalName) {
        int dotIndex = originalName.lastIndexOf('.');
        if (dotIndex > 0) {
            String nameWithoutExt = originalName.substring(0, dotIndex);
            String extension = originalName.substring(dotIndex);
            return nameWithoutExt + " - 副本" + extension;
        } else {
            return originalName + " - 副本";
        }
    }

    /**
     * 生成唯一文件名，如果文件名重复则自动添加序号
     * 例如: 文件.jpg -> 文件(1).jpg -> 文件(2).jpg
     */
    private String generateUniqueFileName(String originalFileName, Long parentId, Long userId) {
        // 检查原文件名是否已存在
        if (!isFileNameExists(originalFileName, parentId, userId)) {
            return originalFileName;
        }

        // 分离文件名和扩展名
        String nameWithoutExt;
        String extension;
        int lastDotIndex = originalFileName.lastIndexOf('.');
        if (lastDotIndex > 0) {
            nameWithoutExt = originalFileName.substring(0, lastDotIndex);
            extension = originalFileName.substring(lastDotIndex);
        } else {
            nameWithoutExt = originalFileName;
            extension = "";
        }

        // 生成带序号的文件名
        int counter = 1;
        String newFileName;
        do {
            newFileName = nameWithoutExt + "(" + counter + ")" + extension;
            counter++;
        } while (isFileNameExists(newFileName, parentId, userId));

        return newFileName;
    }

    /**
     * 检查指定文件夹中是否存在指定文件名
     */
    private boolean isFileNameExists(String fileName, Long parentId, Long userId) {
        List<FileInfo> existingFiles;
        if (parentId == null) {
            existingFiles = fileInfoRepository
                .findByOwnerIdAndParentIdIsNullAndOriginalNameAndIsDeletedFalse(userId, fileName);
        } else {
            existingFiles = fileInfoRepository
                .findByOwnerIdAndParentIdAndOriginalNameAndIsDeletedFalse(userId, parentId, fileName);
        }
        return !existingFiles.isEmpty();
    }
}
